/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.semi.fw;

import com.semi.lex.IntToken;
import com.semi.lex.SemiLexer;
import com.semi.lex.Token;
import com.semi.util.FileLogger;
import com.semi.util.FormatException;
import com.semi.util.IPv4Util;
import com.semi.util.StringUtil;
import com.semi.util.TimeUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;

/**
 *
 * @author liaoyq
 */
public class FwCmd {
    public static final String MSG = "******************************************************************************" +StringUtil.CRLF+
                                      "* Copyright (c) 198-2015 atdmliao. Co., Ltd. All rights reserved.            *" +StringUtil.CRLF+
                                      "* Conent Fire Wall is present,   version 2.0.0.3                             *" +StringUtil.CRLF+
                                      "* no decompiling or reverse-engineering shall be allowed.                    *" +StringUtil.CRLF+
                                      "******************************************************************************"+StringUtil.CRLF;     
    private final static int ERR_OK =   1;
    private final static int ERR_LEXER = -1;
    private final static int ERR_SYNTAX = -2;
    private final static int ERR_CMD =  -3;
    private final static int ERR_EXEC =  -4;
    private final FileLogger mLogger = new FileLogger(FwCmd.class.getName());
    private CodeCenter mRoot ;
    
    public FwCmd(CodeCenter root){
        mRoot =root;
    }

    protected String Error(int err,int pos,String msg){
	switch(err){
	case 0: return "";
	case ERR_OK: return "command ok: "+msg;
	case ERR_LEXER:
	case ERR_SYNTAX:
		return "bad syntax["+pos+"%]: "+msg;
	case ERR_EXEC:
		return "failure["+pos+"]: "+msg;
	default:
		return "bad command["+pos+"]: "+msg;
	}        
    }
    private int FindCmd(Token t,int crr){
        String tmp = t.toString();
        if( tmp.compareToIgnoreCase("sys")==0) return 1;
        if( tmp.compareToIgnoreCase("quit")==0) return 2;
        if( tmp.compareToIgnoreCase("exit")==0) return 2;
        if( tmp.compareToIgnoreCase("save")==0) return 3;
        if( tmp.compareToIgnoreCase("bye")==0) return 4;
        if( tmp.compareToIgnoreCase("cls")==0) return 5;
        if( tmp.compareToIgnoreCase("sysdown")==0) return 6;
        if( tmp.compareToIgnoreCase("pwd")==0) return 8;

        if( tmp.compareToIgnoreCase("cd")==0) return 51;
        if( tmp.compareToIgnoreCase("list")==0) return 50;
        if( tmp.compareToIgnoreCase("ls")==0) return 50;
        
        if( crr==0 ) crr=100; else crr=0;
        
        if( tmp.compareToIgnoreCase("set")==0) return crr+9;
        else if( tmp.compareToIgnoreCase("add")==0) return crr+10;
        else if( tmp.compareToIgnoreCase("mk")==0) return crr+10;
        else if( tmp.compareToIgnoreCase("del")==0) return crr+11;
        else if( tmp.compareToIgnoreCase("rm")==0) return crr+11;
        
        return 0;       
    }
    public String execute(String cmd,FwUser user) throws IOException {
        if( user==null ) return "";
        SemiLexer lex = new SemiLexer(new BufferedReader( new StringReader(cmd) ));
        Token t;
        try {
            t = lex.scan();
        
            if( t==null ) return "";
            if( t.tag()!= Token.ID ) return Error(ERR_SYNTAX,lex.Offset(),"expect command name");
            int param1 = FindCmd(t,user.mSection&0x0003);
            if( param1 <1) return Error(ERR_CMD,lex.Offset(),"unrecognized command '"+t+"'" ); 
            if( (user.mSection& 0x8000 )==0 ) {
               if( param1!=1) return Error(ERR_EXEC,lex.Offset(),"not permit" ); 
                user.mSection =0x8000;
                return Error(ERR_OK,0,"succeed");  
            }
            if( param1<100 ){
                if( param1<50){
                   t = lex.scan();
                   if( t!=null ) return Error(ERR_CMD,lex.Offset(),"too many parameter");
                }
                switch(param1){
                case 2:
                    if( (user.mSection & 0x8000)!=0 ) user.mSection &=~0x8000;
                    else  user.SetExit();
                    break;
                case 3:  
                    String tmp =mRoot.save();
                    return Error(ERR_CMD,0,tmp);
                case 4:
                    user.SetExit();
                    break;
                case 5:
                    user.cls();
                    break;
                case 6:
                    System.exit(0);
                case 8:
                    if( (user.mSection & 0x0003)==0 )  return Error(ERR_OK,0,"/"); 
                     else if( (user.mSection & 0x0003)==1 )  return Error(ERR_OK,0,"/ip"); 
                     else if( (user.mSection & 0x0003)==2 )  return Error(ERR_OK,0,"/code");                              
                break;
                case 9:
                    return SetCommand(lex,user.mSection &0x0003);
                case 10:
                    return AddCommand(lex,user.mSection &0x0003);
                case 11:
                    return DelCommand(lex,user.mSection & 0x0003);
                case 50:
                    return ListCommand(lex,user.mSection & 0x0003);
                case 51:
                    return CdCommand(lex,user);
                default:
                    return Error(ERR_CMD,lex.Offset(),"unsupport command");
                }                        
                return Error(ERR_OK,0,"succeed");            
            }        
            t = lex.scan();
      
            if( t==null ) return Error(ERR_SYNTAX,lex.Offset(),"unexpect end");
            int type =0;
            if( t.tag()!=Token.ID ) return Error( ERR_SYNTAX,lex.Offset(),"expect ip or code");
            if( "ip".compareToIgnoreCase(t.toString())==0) type = 1;
            else if( "code".compareToIgnoreCase(t.toString())==0) type = 2;
            if( type==0 )  return Error( ERR_SYNTAX,lex.Offset(),"expect ip or code");

            //if(param1 == 108 ) return ListCommand(lex,type);
            if( param1== 109 ) return SetCommand(lex, type);
            if( param1== 110 ) return AddCommand(lex,type);
            if( param1== 111 ) return DelCommand(lex,type);
            return Error(ERR_OK,0,"succeed");
        } catch (FormatException ex) {
            return Error(ERR_SYNTAX,lex.Offset(),ex.getMessage());
        }
    }    

    private String ListObject(int type,int start,int end){
        StringBuilder tmp = new StringBuilder();
        if( type==0 ){
            tmp.append(" ip \t code");
        }else if( type==1){
            tmp.append(" index \t host \t ttl \t enable");
            UnCoderIP data;
            for(int i=0;i< mRoot.mIPs.size();i++){
                data = mRoot.mIPs.get(i);
                if( data==null ) continue;
                tmp.append( StringUtil.CRLF+i+"\t"+IPv4Util.intToIp(data.mIP)+"/"+IPv4Util.maskToInt(data.mMask)
                        +"\t"+TimeUtil.getTime(data.mTTL)+"\t"+data.mEnable);
            }
        } else {
            tmp.append(" index \t name \t text \t enable");
            UnCoder data ;
            for(int i=0;i<mRoot.mCoders.size();i++){
                data = mRoot.mCoders.get(i);
                if( data==null ) continue;
                tmp.append( StringUtil.CRLF+i +"\t"+data.mName+"\t"+data.mText+"\t"+data.mEnable);
            }
        }
        return Error(ERR_OK,0,"succeed\r\n")+tmp.toString();
    }
    private String CdCommand(SemiLexer lex ,FwUser user)throws FormatException, IOException {
            Token     t = lex.scan();
            if( t==null ) return Error(ERR_SYNTAX,lex.Offset(),"unexpect end");
            int type =0;
            if( t.tag()!=Token.ID ) return Error( ERR_SYNTAX,lex.Offset(),"expect ip or code");
            if( "ip".compareToIgnoreCase(t.toString())==0) type = 1;
            else if( "code".compareToIgnoreCase(t.toString())==0) type = 2;
            else if( "..".compareToIgnoreCase(t.toString())==0) type = 3;
            if( type==0 )  return Error( ERR_SYNTAX,lex.Offset(),"expect ip or code");
            int k = user.mSection &0x0003;
            switch(type){
                case 1:
                    if( k==0 ) user.mSection |=0x0001;
                    else return Error( ERR_CMD,lex.Offset(),"ip not found");
                    break;
                case 2:
                    if( k==0 ) user.mSection |=0x0002;
                    else return Error( ERR_CMD,lex.Offset(),"code not found");
                    break;
                case 3:
                    if( k!=0 ) user.mSection &=~0x0003;
                    else user.mSection =0;
                    break;
            }
            return Error(ERR_OK,0,"succeed");
    }    
    protected String ListCommand(SemiLexer lex,int type) throws FormatException, IOException {
        // list ip/code 
        // list ip/code start end
        Token t  = lex.scan();
        if( t==null )return ListObject(type,0,-1);
        if(type==0){
            if( t.tag()!=Token.ID ) return Error( ERR_SYNTAX,lex.Offset(),"expect ip or code");
            if( "ip".compareToIgnoreCase(t.toString())==0) type = 1;
            else if( "code".compareToIgnoreCase(t.toString())==0) type = 2;
            if( type==0 )  return Error( ERR_SYNTAX,lex.Offset(),"expect ip or code");        
        }
        if( t.tag() != Token.INT)  return Error( ERR_SYNTAX,lex.Offset(),"expect int");
        int x=0, y=-1;
        x = ((IntToken)t).Value();
        t = lex.scan();
        if( t!=null ){
            if( t.tag()!=Token.INT ) return Error( ERR_SYNTAX,lex.Offset(),"expect int");
            y = ((IntToken)t).Value();
        }
        return ListObject(type,x,y);
    }
    protected String SetCommand(SemiLexer lex,int type) throws FormatException, IOException {
        // set ip/code index xxxx field value
        Token t = lex.scan();
        if( t==null )  return Error(ERR_SYNTAX,lex.Offset(),"unexpect end");
        if( (t.tag()!=Token.ID) || ("index".compareToIgnoreCase(t.toString())!=0) ) 
                return  Error(ERR_SYNTAX,lex.Offset(),"expect 'index' here ");
        t = lex.scan();
        if( t==null ) return Error(ERR_SYNTAX,lex.Offset(),"unexpect end");
        if( t.tag()!=Token.INT ) return Error(ERR_SYNTAX,lex.Offset(),"expect integer here");
        int k = ((IntToken)t).Value();        
        
        t = lex.scan();
        if( t==null ) return Error(ERR_SYNTAX,lex.Offset(),"unexpect end");
        if( t.tag()!=Token.ID ) return  Error(ERR_SYNTAX,lex.Offset(),"missing field name");
        String field = t.toString();
        t = lex.scan();
        if( t==null ) return Error(ERR_SYNTAX,lex.Offset(),"unexpect end");
        String value = t.toString();
        
        if( lex.scan()!=null )  return Error(ERR_SYNTAX,lex.Offset(),"too many parameter");
        UnRule data ;
        if( type==1 ){
            if( k<0|| k>=mRoot.mIPs.size() ) return Error(ERR_EXEC,lex.Offset(),"index out of range");
            data =  mRoot.mIPs.get(k);
        } else{
            if( k<0|| k>=mRoot.mCoders.size() ) return  Error(ERR_EXEC,lex.Offset(),"index out of range");
            data =  mRoot.mCoders.get(k);
        }
        if( data==null ) return  Error(ERR_EXEC,lex.Offset(), "index out of range");
        k= data.setProp(field, value);
        if( k>0 ) return  Error(ERR_OK,0,"succeed");
        if( k==0 ) return Error(ERR_EXEC,lex.Offset(),"bad value");
        return Error(ERR_EXEC,lex.Offset(),"not support field "+field);
    }
    protected String AddCommand(SemiLexer lex,int type) throws FormatException, IOException {
        //add ip/code field1 value1 field2 value2
        Token t = lex.scan();
        if( t==null ) return Error(ERR_SYNTAX,lex.Offset(),"unexpect end");
        UnRule data; 
        if( type==1 )    data = new UnCoderIP();
        else data = new UnCoder();
        int k;
        do{
            if( t.tag()!=Token.ID ) return  Error(ERR_SYNTAX,lex.Offset(),"missing field name");
            String field = t.toString();
            t = lex.scan();
            if( t==null ) return  Error(ERR_SYNTAX,lex.Offset(),"unexpect end");       
            String value = t.toString();
            try{
                k = data.setProp(field, value);
                if( k==0 ) return Error(ERR_EXEC,lex.Offset(),"bad parameter["+t.toString()+"] for field "+field);
                if( k<0 ) return Error(ERR_EXEC,lex.Offset(),"not support field '"+"'");
            }catch(FormatException e){
                return  Error(ERR_EXEC,lex.Offset(),e.getMessage()+" for field "+field);
            }
        } while( ( t= lex.scan())!=null);
        if( type==1 ){
            if(  ((UnCoderIP)data).mIP ==0 ) 
              return Error(ERR_EXEC,lex.Offset(),"missing host");
            mRoot.mIPs.add((UnCoderIP)data);
        }  else{
            mRoot.mCoders.add((UnCoder)data);
        }
        return  Error(ERR_OK,0,"succeed");
    }    
    protected String DelCommand(SemiLexer lex,int type) throws IOException, FormatException{
            // del ip/code index
        Token t = lex.scan();
        if( t==null ) return Error(ERR_SYNTAX,lex.Offset(),"unexpect end"); 
        if( t.tag()!=Token.INT ) return Error(ERR_SYNTAX,lex.Offset(),"expect number");
        int  k = ((IntToken)t).Value();
        if( k<0 ) return Error(ERR_EXEC,lex.Offset(),"index out of range ");
        UnRule data; 
        if( type==1){
            if( mRoot.mIPs.size()<=k )  return Error(ERR_EXEC,lex.Offset(),"index out of range ");
            data = mRoot.mIPs.get(k);
        } else{
            if( mRoot.mCoders.size()<=k )  return Error(ERR_EXEC,lex.Offset(),"index out of range ");
            data = mRoot.mCoders.get(k);
        }
        return Error(ERR_OK,0,"succeed");
    }

}
